#pragma once

#include "../common/Log.hpp"
#include "../common/SqliteHelper.hpp"
#include "../common/FileHelper.hpp"
#include "../common/message.pb.h"
#include "Binding.hpp"
#include "BindingMapper.hpp"
#include <iostream>
#include <string>
#include <unordered_map>
#include <mutex>
#include <memory>

namespace micromq
{
    class BindingManager
    {
    public:
        using ptr = std::shared_ptr<BindingManager>;
    public:
        BindingManager(const std::string& dbFileName): _mapper(dbFileName)
        {
            _bindings = _mapper.selectAll();
        }

        bool bind(const std::string& exchangeName, const std::string& msgQueueName, const std::string& bindingKey, bool durable)
        {
            std::unique_lock<std::mutex> lock(_mtx);
            auto it = _bindings.find(exchangeName);
            if(it != _bindings.end() && it->second.find(msgQueueName) != it->second.end()) return false;

            Binding::ptr binding = std::make_shared<Binding>(exchangeName, msgQueueName, bindingKey, durable);
            if(durable == true) _mapper.insertBinding(binding);
            BindingInfo& bindingInfo = _bindings[exchangeName]; //从_bindings中取防止覆盖历史数据，无则新建
            bindingInfo[msgQueueName] = binding;
            return true;
        }

        void unbindByEntry(const std::string& exchangeName, const std::string& msgQueueName)
        {
            std::unique_lock<std::mutex> lock(_mtx);
            auto it = _bindings.find(exchangeName);
            if(it == _bindings.end() || it->second.find(msgQueueName) == it->second.end()) return;
            
            if(it->second[msgQueueName]->_durable == true) _mapper.deleteBindingByEntry(exchangeName, msgQueueName);
            _bindings[exchangeName].erase(msgQueueName);
        }

        void unbindByEcxchange(const std::string& exchangeName)
        {
            std::unique_lock<std::mutex> lock(_mtx);
            if(_bindings.find(exchangeName) == _bindings.end()) return;
            _mapper.deleteBindingByEcxchange(exchangeName);
            _bindings.erase(exchangeName);
        }

        void unbindByMsgQueue(const std::string& msgQueueName)
        {
            std::unique_lock<std::mutex> lock(_mtx);
            for(auto start = _bindings.begin(); start != _bindings.end(); ++start) 
            {
                if(start->second.find(msgQueueName) != start->second.end())
                {
                    _mapper.deleteBindingByMsgQueue(msgQueueName);
                    start->second.erase(msgQueueName);
                }
            }
        }

        BindingInfo getBindingInfoByEcxchange(const std::string& exchangeName)
        {
            std::unique_lock<std::mutex> lock(_mtx);
            auto it = _bindings.find(exchangeName);
            if(it == _bindings.end()) return BindingInfo();
            return it->second;
        }

        Binding::ptr getBindingByEntry(const std::string& exchangeName, const std::string& msgQueueName)
        {
            std::unique_lock<std::mutex> lock(_mtx);
            auto it1 = _bindings.find(exchangeName);
            if(it1 != _bindings.end())
            {
                auto it2 = it1->second.find(msgQueueName);
                if(it2 != it1->second.end())
                {
                    return it2->second;
                }
            }
            return nullptr;
        }

        BindingMap getAllBinding()
        {
            std::unique_lock<std::mutex> lock(_mtx);
            return _bindings;
        }

        bool isExists(const std::string& exchangeName, const std::string& msgQueueName)
        {
            std::unique_lock<std::mutex> lock(_mtx);
            auto it = _bindings.find(exchangeName);
            return it != _bindings.end() && it->second.find(msgQueueName) != it->second.end();
        }

        int size()
        {
            std::unique_lock<std::mutex> lock(_mtx);
            int ret = 0;
            for(auto start = _bindings.begin(); start != _bindings.end(); ++start) 
            {
                ret += start->second.size();
            }
            return ret;
        }

        void clear()
        {
            std::unique_lock<std::mutex> lock(_mtx);
            _mapper.removeTable();
            _bindings.clear();
        }

        ~BindingManager()
        {}
    private:
        BindingMapper _mapper;
        BindingMap _bindings;
        std::mutex _mtx;
    };
}